﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Icinetic.FeedCategorySample.CrossCutting;
using Icinetic.FeedCategorySample.Entities;
using Icinetic.FeedCategorySample.Services.Providers;
using Icinetic.FeedCategorySample.Services.Repositories;

namespace Icinetic.FeedCategorySample.Services
{
    public class ContentService
    {
        private FeedsProvider feedsProvider;
        private IRepository<RssItem> rssItemsRepository;

        private string feedUrl;
        private Dictionary<string, string> feedUrlMultiple;

        public static Queue<Tuple<string, Action>> writeOperations = new Queue<Tuple<string, Action>>();
        public static Queue<Tuple<string, Action>> cacheImageOperations = new Queue<Tuple<string, Action>>();

        public ContentService(string feedUrl)
        {
            this.feedUrl = feedUrl;
            this.feedsProvider = new FeedsProvider();
            this.rssItemsRepository = new RssItemRepository();
        }

        public ContentService(Dictionary<string, string> feedUrlMultiple)
        {
            this.feedUrlMultiple = feedUrlMultiple;
            this.feedsProvider = new FeedsProvider();
            this.rssItemsRepository = new RssItemRepository();
        }

        public async Task<Dictionary<string, List<RssItem>>> GetItems<T>(Dictionary<string, List<RssItem>> hostCollection, int count = 0, int index = 0) where T : Entity
        {
            List<RssItem> feedItems = rssItemsRepository.FindAll() as List<RssItem>;
            
            if (feedItems == null || feedItems.Count==0)
            {
                //Obtener desde el feed
                feedItems = await feedsProvider.Get<T>(feedUrl, count, index);
                //Añadir elementos a la colección
                AddItemsToHostCollection(hostCollection, feedItems);
                //Guardar elementos en BD
                AddItemsToDB<T>(feedItems, feedsProvider.GetCacheExpiration());
            }
            else
            {
                AddItemsToHostCollection(hostCollection, feedItems);
                //El último artículo introducido en BD ha alcanzado su fecha de expiración
                if (feedItems.Count > 0 && feedItems.FirstOrDefault().ExpirationDate < DateTime.Now)
                {
                    //Guardar elementos a BD
                    List<RssItem> feedItemsAux = await feedsProvider.Get<T>(feedUrl);
                    AddNewItemsToHostCollection(hostCollection, feedItems, feedItemsAux);
                    AddItemsToDB<T>(feedItemsAux, feedsProvider.GetCacheExpiration());
                }
            }

            return hostCollection;
        }

        public async Task<Dictionary<string, List<RssItem>>> GetItemsMultipleUrl<T>(Dictionary<string, List<RssItem>> hostCollection, int count = 0, int index = 0) where T : Entity
        {
            List<RssItem> feedItems = rssItemsRepository.FindAll() as List<RssItem>;

            if (feedItems == null || feedItems.Count == 0)
            {
                foreach (KeyValuePair<string, string> value in feedUrlMultiple)
                    feedItems.AddRange(await feedsProvider.Get<T>(value.Value, value.Key, count, index));
                AddItemsToHostCollection(hostCollection, feedItems);
                AddItemsToDB<T>(feedItems, feedsProvider.GetCacheExpiration());
            }
            else
            {
                AddItemsToHostCollection(hostCollection, feedItems);
                if (feedItems.Count > 0 && feedItems.FirstOrDefault().ExpirationDate < DateTime.Now)
                {
                    List<RssItem> feedItemsAux = new List<RssItem>();
                    foreach (KeyValuePair<string, string> value in feedUrlMultiple)
                        feedItemsAux.AddRange(await feedsProvider.Get<T>(value.Value, value.Key, count, index));

                    AddNewItemsToHostCollection(hostCollection, feedItems, feedItemsAux);
                    AddItemsToDB<T>(feedItemsAux, feedsProvider.GetCacheExpiration());
                }
            }

            return hostCollection;
        }

        public static void CheckRepositoryLimits<T>()
        {
            int DAYS = 15;
            try
            {
                IRepository<T> repository = new RssItemRepository() as IRepository<T>;
                List<RssItem> feedItems = repository.FindAll() as List<RssItem>;
                foreach (RssItem r in feedItems)
                {
                    if (r.ExpirationDate.Date.CompareTo(DateTime.Now.Date) == -1)
                    {
                        TimeSpan ts = DateTime.Now.Date - r.ExpirationDate.Date;
                        if (ts.Days > DAYS)
                            repository.Remove(r as RssItem);
                    }
                }
            }
            catch (Exception ex)
            {
                LoggingService.Log(ex);
            }
        }

        private static void AddNewItemsToHostCollection(Dictionary<string, List<RssItem>> hostCollection, List<RssItem> feedItems, List<RssItem> feedItemsAux)
        {
            foreach (RssItem t in feedItemsAux)
                if (!feedItems.Contains(t))
                {
                    if (hostCollection.ContainsKey(t.Category))
                        hostCollection[t.Category].Add(t);
                    else
                    {
                        List<RssItem> list = new List<RssItem>();
                        list.Add(t);
                        hostCollection.Add(t.Category, list);
                    }
                }
        }

        private static void AddItemsToHostCollection(Dictionary<string, List<RssItem>> hostCollection, List<RssItem> feedItems)
        {
            foreach (RssItem t in feedItems)
            {
                if (hostCollection.ContainsKey(t.Category))
                    hostCollection[t.Category].Add(t);
                else
                {
                    List<RssItem> list = new List<RssItem>();
                    list.Add(t);
                    hostCollection.Add(t.Category, list);
                }
            }
        }

        private void AddItemsToDB<T>(List<RssItem> feedItems, DateTime dateCacheExpiration) where T : Entity
        {
            foreach (RssItem f in feedItems)
                f.ExpirationDate = dateCacheExpiration;

            rssItemsRepository.Add(feedItems);
        }
    }
}
